home *** CD-ROM | disk | FTP | other *** search
/ ADA Programming Guide / ADA Programming Guide.iso / ada_gnu / adainc / a-sequio.adb < prev    next >
Text File  |  1996-01-30  |  16KB  |  500 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUNTIME COMPONENTS                          --
  4. --                                                                          --
  5. --                    A D A . S E Q U E N T I A L _ I O                     --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.4 $                              --
  10. --                                                                          --
  11. --           Copyright (c) 1992,1993,1994 NYU, All Rights Reserved          --
  12. --                                                                          --
  13. -- The GNAT library is free software; you can redistribute it and/or modify --
  14. -- it under terms of the GNU Library General Public License as published by --
  15. -- the Free Software  Foundation; either version 2, or (at your option) any --
  16. -- later version.  The GNAT library is distributed in the hope that it will --
  17. -- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
  18. -- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
  19. -- Library  General  Public  License for  more  details.  You  should  have --
  20. -- received  a copy of the GNU  Library  General Public License  along with --
  21. -- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
  22. -- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
  23. --                                                                          --
  24. ------------------------------------------------------------------------------
  25.  
  26. with Ada.Storage_IO;
  27. with Interfaces.C;          use Interfaces.C;
  28. with Interfaces.C.Strings;  use Interfaces.C.Strings;
  29. with System.File_Aux;       use System.File_Aux;
  30.  
  31. package body Ada.Sequential_IO is
  32.  
  33.    package Stor_IO is new Ada.Storage_IO (Element_Type => Element_Type);
  34.  
  35.    type Pstring is access String;
  36.  
  37.    type File_Control_Block is record
  38.       Name       : chars_ptr := Null_Ptr;
  39.       Mode       : File_Mode;
  40.       Form       : Pstring;
  41.       Descriptor : C_File_Ptr;
  42.       Byte_Size  : C_Long_Int;
  43.       Byte_Index : C_Long_Int;
  44.    end record;
  45.  
  46.    type Open_Type is (Create, Open);
  47.  
  48.    type C_Mode_Type is array (Open_Type, File_Mode) of chars_ptr;
  49.  
  50.    C_Mode : C_Mode_Type := (others => (others => Null_Ptr));
  51.  
  52.    Buffer : Stor_IO.Buffer_Type;
  53.  
  54.    -----------------------
  55.    -- Local Subprograms --
  56.    -----------------------
  57.  
  58.    procedure Confirm_File_Is_Open (File : in File_Type);
  59.    pragma Inline (Confirm_File_Is_Open);
  60.    --  Checks to make sure the given file is open.
  61.    --  If not, it raises Status_Error.
  62.  
  63.    procedure Confirm_File_Is_Closed (File : in File_Type);
  64.    pragma Inline (Confirm_File_Is_Closed);
  65.    --  Checks to make sure the given file is closed.
  66.    --  If not, it raises Status_Error.
  67.  
  68.    function Current_Size_Of (File : in File_Type) return C_Long_Int;
  69.    --  Returns the current size in bytes of the external file that is
  70.    --  associated with the given file.  The given file must be open.
  71.  
  72.    function New_Temp_File_Name return chars_ptr;
  73.    --  Returns a name that is a valid file name and that is not the same as
  74.    --  the name of an existing external file.
  75.  
  76.    function File_Exists (Name : in String) return Boolean;
  77.    --  Returns True if an external file of the given name exists.
  78.    --  Otherwise, it returns False.
  79.  
  80.    -----------
  81.    -- Close --
  82.    -----------
  83.  
  84.    procedure Close  (File : in out File_Type) is
  85.    begin
  86.       Confirm_File_Is_Open (File);
  87.  
  88.       if C_Fclose (File.Descriptor) /= 0 then
  89.          raise Device_Error;
  90.       end if;
  91.  
  92.       File := null;
  93.    end Close;
  94.  
  95.    --------------------------
  96.    -- Confirm_File_Is_Open --
  97.    --------------------------
  98.  
  99.    procedure Confirm_File_Is_Open (File : in File_Type) is
  100.    begin
  101.       if not Is_Open (File) then
  102.          raise Status_Error;
  103.       end if;
  104.    end Confirm_File_Is_Open;
  105.  
  106.    ----------------------------
  107.    -- Confirm_File_Is_Closed --
  108.    ----------------------------
  109.  
  110.    procedure Confirm_File_Is_Closed (File : in File_Type) is
  111.    begin
  112.       if Is_Open (File) then
  113.          raise Status_Error;
  114.       end if;
  115.    end Confirm_File_Is_Closed;
  116.  
  117.    ------------
  118.    -- Create --
  119.    ------------
  120.  
  121.    procedure Create
  122.      (File : in out File_Type;
  123.       Mode : in File_Mode := Out_File;
  124.       Name : in String := "";
  125.       Form : in String := "")
  126.    is
  127.    begin
  128.       Confirm_File_Is_Closed (File);
  129.       File := new File_Control_Block;
  130.  
  131.       --  A null string for Name specifies creation of a temporary file.
  132.  
  133.       if Name'Length = 0 then
  134.          File.Name := New_Temp_File_Name;
  135.       else
  136.          File.Name := New_String (Name);
  137.       end if;
  138.  
  139.       File.Descriptor := C_Fopen (Filename => File.Name,
  140.                                   Mode     => C_Mode (Create, Mode));
  141.  
  142.       --  If the C fopen call fails, it returns a null pointer.
  143.  
  144.       if C_Void_Ptr (File.Descriptor) = C_Null then
  145.          raise Name_Error;
  146.       end if;
  147.  
  148.       File.Mode := Mode;
  149.       File.Form := new String'(Form);
  150.  
  151.       --  The size of the external file is required in order to avoid
  152.       --  lookahead.  In C, the end-of-file indicator is not considered to
  153.       --  be true until after an attempt is made to read past the end of the
  154.       --  external file.  In Ada, the End_Of_File function returns True if no
  155.       --  more elements can be read (i.e. when reading elements, End_Of_File
  156.       --  becomes True before a failed read caused by end-of-file).  In
  157.       --  Sequential_IO, it is sufficient to determine the size of the
  158.       --  external file once at the time of the opening of the file.  The
  159.       --  End_Of_File function only operates on a file of mode In_File, and
  160.       --  such a file will not change in size.
  161.  
  162.       File.Byte_Size  := Current_Size_Of (File);
  163.       File.Byte_Index := 0;
  164.    end Create;
  165.  
  166.    ---------------------
  167.    -- Current_Size_Of --
  168.    ---------------------
  169.  
  170.    function Current_Size_Of (File : in File_Type) return C_Long_Int is
  171.       Current_Byte_Index : C_Long_Int;
  172.       Current_Byte_Size  : C_Long_Int;
  173.  
  174.    begin
  175.       Current_Byte_Index := C_Ftell (File.Descriptor);
  176.  
  177.       if C_Fseek (Stream => File.Descriptor,
  178.                   Offset => 0,
  179.                   Whence => C_Seek_End) /= 0 then
  180.          raise Device_Error;
  181.       end if;
  182.  
  183.       Current_Byte_Size := C_Ftell (File.Descriptor);
  184.  
  185.       if C_Fseek (Stream => File.Descriptor,
  186.                   Offset => Current_Byte_Index,
  187.                   Whence => C_Seek_Set) /= 0 then
  188.          raise Device_Error;
  189.       end if;
  190.  
  191.       return Current_Byte_Size;
  192.    end Current_Size_Of;
  193.  
  194.    ------------
  195.    -- Delete --
  196.    ------------
  197.  
  198.    procedure Delete (File : in out File_Type) is
  199.       File_Name_To_Delete : chars_ptr;
  200.  
  201.    begin
  202.       Confirm_File_Is_Open (File);
  203.  
  204.       --  The file should be closed before calling the C remove function.
  205.       --  If the file is open, the behavior of the remove function is
  206.       --  implementation-defined.  Closing the file, however, means we
  207.       --  lose the info in the file control block, so we have to save the
  208.       --  file name temporarily in order to have it for use with the remove
  209.       --  function.
  210.  
  211.       File_Name_To_Delete := File.Name;
  212.       Close (File);
  213.  
  214.       if C_Remove (File_Name_To_Delete) /= 0 then
  215.          raise Use_Error;
  216.       end if;
  217.    end Delete;
  218.  
  219.    -----------------
  220.    -- End_Of_File --
  221.    -----------------
  222.  
  223.    function End_Of_File (File : in File_Type) return Boolean is
  224.    begin
  225.       Confirm_File_Is_Open (File);
  226.  
  227.       if File.Mode /= In_File then
  228.          raise Mode_Error;
  229.       end if;
  230.  
  231.       return File.Byte_Index >= File.Byte_Size;
  232.    end End_Of_File;
  233.  
  234.    -----------------
  235.    -- File_Exists --
  236.    -----------------
  237.  
  238.    function File_Exists (Name : in String) return Boolean is
  239.       File_Descriptor : C_File_Ptr;
  240.       C_Name          : chars_ptr;
  241.  
  242.    begin
  243.       C_Name := New_String (Name);
  244.       File_Descriptor := C_Fopen (Filename => C_Name,
  245.                                   Mode     => C_Mode (Open, In_File));
  246.  
  247.       if C_Void_Ptr (File_Descriptor) = C_Null then
  248.          return False;
  249.       end if;
  250.  
  251.       if C_Fclose (File_Descriptor) /= 0 then
  252.          raise Device_Error;
  253.       end if;
  254.  
  255.       return True;
  256.    end File_Exists;
  257.  
  258.    ----------
  259.    -- Form --
  260.    ----------
  261.  
  262.    function Form (File : in File_Type) return String is
  263.    begin
  264.       Confirm_File_Is_Open (File);
  265.       return File.Form.all;
  266.    end Form;
  267.  
  268.    -------------
  269.    -- Is_Open --
  270.    -------------
  271.  
  272.    function Is_Open (File : in File_Type) return Boolean is
  273.    begin
  274.       return File /= null;
  275.    end Is_Open;
  276.  
  277.    ----------
  278.    -- Mode --
  279.    ----------
  280.  
  281.    function Mode (File : in File_Type) return File_Mode is
  282.    begin
  283.       Confirm_File_Is_Open (File);
  284.       return File.Mode;
  285.    end Mode;
  286.  
  287.    ----------
  288.    -- Name --
  289.    ----------
  290.  
  291.    function Name (File : in File_Type) return String is
  292.    begin
  293.       Confirm_File_Is_Open (File);
  294.       return Value (File.Name);
  295.    end Name;
  296.  
  297.    ------------------------
  298.    -- New_Temp_File_Name --
  299.    ------------------------
  300.  
  301.    function New_Temp_File_Name return chars_ptr is
  302.       Temp_File_Name   : String := "ADATMPXX";
  303.       C_Temp_File_Name : chars_ptr;
  304.  
  305.    begin
  306.       C_Temp_File_Name := New_String (Temp_File_Name);
  307.       C_Temp_File_Name := C_Mktemp (C_Temp_File_Name);
  308.       return C_Temp_File_Name;
  309.    end New_Temp_File_Name;
  310.  
  311.    ----------
  312.    -- Open --
  313.    ----------
  314.  
  315.    procedure Open
  316.      (File : in out File_Type;
  317.       Mode : in File_Mode;
  318.       Name : in String;
  319.       Form : in String := "")
  320.    is
  321.    begin
  322.       Confirm_File_Is_Closed (File);
  323.  
  324.       --  The language standard specifies that Name_Error must be raised if
  325.       --  no external file with the given name exists.  This should occur
  326.       --  regardless of the given mode.  The mode argument to the C fopen
  327.       --  function does not have sufficient flexibility to handle this
  328.       --  behavior with one call to fopen.  In particular, opening a file with
  329.       --  mode Out_File should fail if the external file does not exist, but
  330.       --  should open and truncate the external file if it exists.  The C
  331.       --  fopen funcation has no direct equivalent of this, as an fopen with
  332.       --  write mode succeeds whether the file exists or not.  In order to
  333.       --  get the desired behavior in Ada, we need to do a separate check for
  334.       --  file existence prior to the C fopen call to open the file.
  335.  
  336.       if not File_Exists (Name) then
  337.          raise Name_Error;
  338.       end if;
  339.  
  340.       File := new File_Control_Block;
  341.  
  342.       File.Name := New_String (Name);
  343.       File.Descriptor := C_Fopen (Filename => File.Name,
  344.                                   Mode     => C_Mode (Open, Mode));
  345.  
  346.       --  If the C fopen call fails, it returns a null pointer.
  347.  
  348.       if C_Void_Ptr (File.Descriptor) = C_Null then
  349.          raise Name_Error;
  350.       end if;
  351.  
  352.       File.Mode := Mode;
  353.       File.Form := new String'(Form);
  354.  
  355.       --  The size of the external file is required in order to avoid
  356.       --  lookahead.  In C, the end-of-file indicator is not considered to
  357.       --  be true until after an attempt is made to read past the end of the
  358.       --  external file.  In Ada, the End_Of_File function returns True if no
  359.       --  more elements can be read (i.e. when reading elements, End_Of_File
  360.       --  becomes True before a failed read caused by end-of-file).  In
  361.       --  Sequential_IO, it is sufficient to determine the size of the
  362.       --  external file once at the time of the opening of the file.  The
  363.       --  End_Of_File function only operates on a file of mode In_File, and
  364.       --  such a file will not change in size.
  365.  
  366.       File.Byte_Size  := Current_Size_Of (File);
  367.       File.Byte_Index := 0;
  368.    end Open;
  369.  
  370.    ----------
  371.    -- Read --
  372.    ----------
  373.  
  374.    procedure Read (File : in File_Type; Item : out Element_Type) is
  375.    begin
  376.       Confirm_File_Is_Open (File);
  377.  
  378.       if File.Mode /= In_File then
  379.          raise Mode_Error;
  380.       end if;
  381.  
  382.       if End_Of_File (File) then
  383.          raise End_Error;
  384.       end if;
  385.  
  386.       --  The C fread function returns the number of elements successfully
  387.       --  read.  Since we only read one element at a time and we have already
  388.       --  checked for end of file, if the number of elements successfully read
  389.       --  does not equal the number of elements requested, it is considered to
  390.       --  be a Device_Error.
  391.  
  392.       if C_Fread (Ptr    => C_Void_Ptr (Buffer'Address),
  393.                   Size   => C_Size_T (Buffer'Length),
  394.                   Nmemb  => 1,
  395.                   Stream => File.Descriptor) /= 1
  396.       then
  397.          raise Device_Error;
  398.       end if;
  399.  
  400.       --  Advance the byte index so we can check for end of file.
  401.  
  402.       File.Byte_Index := File.Byte_Index + Buffer'Length;
  403.  
  404.       Stor_IO.Read (Buffer, Item);
  405.    end Read;
  406.  
  407.    -----------
  408.    -- Reset --
  409.    -----------
  410.  
  411.    procedure Reset  (File : in out File_Type; Mode : in File_Mode) is
  412.       Old_File : File_Type := File;
  413.  
  414.    begin
  415.       Confirm_File_Is_Open (File);
  416.       Close (File);
  417.       Open (File, Mode, Value (Old_File.Name), Old_File.Form.all);
  418.    end Reset;
  419.  
  420.    procedure Reset  (File : in out File_Type) is
  421.    begin
  422.       Confirm_File_Is_Open (File);
  423.       Reset (File, File.Mode);
  424.    end Reset;
  425.  
  426.    -----------
  427.    -- Write --
  428.    -----------
  429.  
  430.    procedure Write (File : in File_Type; Item : in Element_Type) is
  431.    begin
  432.       Confirm_File_Is_Open (File);
  433.  
  434.       if File.Mode = In_File then
  435.          raise Mode_Error;
  436.       end if;
  437.  
  438.       Stor_IO.Write (Buffer, Item);
  439.  
  440.       --  The C fwrite function returns the number of elements successfully
  441.       --  written, which will less than the number of elements requested only
  442.       --  if a write error is encountered.  Such a situation is considered to
  443.       --  be a Device_Error.
  444.  
  445.       if C_Fwrite (Ptr    => C_Void_Ptr (Buffer'Address),
  446.                    Size   => C_Size_T (Buffer'Length),
  447.                    Nmemb  => 1,
  448.                    Stream => File.Descriptor) /= 1
  449.       then
  450.          raise Device_Error;
  451.       end if;
  452.    end Write;
  453.  
  454. begin
  455.    -------------------------
  456.    -- Package Elaboration --
  457.    -----------------
  458.    --  The following possible modes for the C fopen function are given here
  459.    --  for reference:
  460.    --
  461.    --  r   open text file for reading
  462.    --  w   truncate to zero length or create text file for writing
  463.    --  a   append; open or create text file for writing at end-of-file
  464.    --  rb  open binary file for reading
  465.    --  wb  truncate to zero length or create binary file for writing
  466.    --  ab  append; open or create binary file for writing at end-of-file
  467.    --  r+  open text file for update (reading and writing)
  468.    --  w+  truncate to zero length or create text file for update
  469.    --  a+  append; open or create text file for update, writing at end-of-file
  470.    --  rb+ open binary file for update (reading and writing)
  471.    --  wb+ truncate to zero length or create binary file for update
  472.    --  ab+ append; open or create binary file for update, writing at
  473.    --      end-of-file
  474.    --
  475.    --  Notes:
  476.    --
  477.    --  (1) Opening a file with read mode fails if the file does not exist or
  478.    --  cannot be read.
  479.    --
  480.    --  (2) Opening a file with append mode causes all subsequent writes to the
  481.    --  file to be forced to the then current end-of-file, regardless of
  482.    --  intervening calls to the fseek function.
  483.    --
  484.    --  (3) When a file is opened with update mode, both input and output may be
  485.    --  performed on the associated stream.  However, output may not be directly
  486.    --  followed by input without an intervening call to the fflush function or
  487.    --  to a file positioning function (fseek, fsetpos, or rewind), and input
  488.    --  may not be directly followed by output without an intervening call to a
  489.    --  file positioning function, unless the input operation encounters
  490.    --  end-of-file.
  491.  
  492.    C_Mode (Create, In_File)     := New_String ("wb");
  493.    C_Mode (Create, Out_File)    := New_String ("wb");
  494.    C_Mode (Create, Append_File) := New_String ("wb");
  495.  
  496.    C_Mode (Open,   In_File)     := New_String ("rb");
  497.    C_Mode (Open,   Out_File)    := New_String ("wb");
  498.    C_Mode (Open,   Append_File) := New_String ("ab");
  499. end Ada.Sequential_IO;
  500.